package scantask.server.domainLayer;

import java.awt.Polygon;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

import scantask.server.dataLayer.DataManipulatorInterface;
import scantask.server.dataLayer.exceptions.GetBenchmarksException;
import scantask.server.dataLayer.exceptions.GetMoistureException;
import scantask.server.dataLayer.exceptions.GetPestException;
import scantask.server.dataLayer.exceptions.GetSyntheticObjectsException;
import scantask.server.dataLayer.exceptions.GetTemperatureException;
import scantask.server.domainLayer.Exceptions.AddBenchmarkException;
import scantask.server.domainLayer.Exceptions.GetUserBenchmarksException;
import scantask.server.utilities.algorithm.InterpolationException;
import scantask.server.utilities.algorithm.InterpolatorInterface;
import scantask.server.utilities.algorithm.ShepardInterpolation;

import scantask.containers.Area;
import scantask.containers.Benchmark;
import scantask.containers.Coordinate;
import scantask.containers.InformationObject;
import scantask.containers.Moisture;
import scantask.containers.Pest;
import scantask.containers.Temperature;
import scantask.containers.WeekDate;
import scantask.containers.ZoomLevel;
import scantask.containers.exceptions.IllegalBenchmarkNameException;
import scantask.containers.exceptions.IllegalLocationNameExeption;
import scantask.containers.exceptions.InvalidCoordinateValueException;
import scantask.containers.exceptions.InvalidMeasurementTypeException;
import scantask.containers.exceptions.UnInitializeObjectException;

public class DataController {
	
	private DataManipulatorInterface _dataManipulator;
	private InterpolatorInterface _interpolator;
	
	private static int MIN_RADIUS = 1;
	private static int MAX_RADIUS = 50;
	
	public DataController(DataManipulatorInterface dataManipulator) {
		
		this._dataManipulator = dataManipulator;
		
		this._interpolator = new ShepardInterpolation();
			
		this.readPestRadiusCFG();
	}
	
	private void readPestRadiusCFG() {
		FileInputStream fstream;
		try {
			fstream = new FileInputStream("resource\\pestRadius.cfg");
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String line = "";
			while ((line = br.readLine()) != null) {
				String[] lineContent = line.split("=");
				if (lineContent.length != 2) {
					scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Illegal content of the pestRadius.cfg file");
					continue;
				}
				if (lineContent[0].compareTo("minRadius") == 0) {
					MIN_RADIUS = new Integer(lineContent[1]).intValue();
				}
				if (lineContent[0].compareTo("maxRadius") == 0) {
					MAX_RADIUS = new Integer(lineContent[1]).intValue();
				}
			}
		} catch (FileNotFoundException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning(e.toString());
		} catch (IOException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning(e.toString());			
		}
			
	}
	
	
	public void addBenchmark(String userName, String locationName, Coordinate coordinate) throws AddBenchmarkException {
		Benchmark benchmark = null;
		try {
			// create new benchmark
			benchmark = new Benchmark(locationName, coordinate);
		} catch (IllegalBenchmarkNameException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning("add benchmark is failed");
			throw new AddBenchmarkException("Error - Add benchmark failed, Illegal benchmark name");
		} catch (UnInitializeObjectException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning("add benchmark is failed");
			throw new AddBenchmarkException("Error - Add benchmark failed, Illegal coordinate");
		}
		
		// add the benchmark to data base
		try {
			this._dataManipulator.addBenchmark(userName, benchmark);
		} catch (scantask.server.dataLayer.exceptions.AddBenchmarkException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning("add benchmark is failed");
			throw new AddBenchmarkException(e.getErrorDescription());
		}

		
	}

	/* already get it in the login
	public Coordinate getDefaultLocation(String userName) throws GetDefaultLocationException {
			try {
				return this._dataManipulator.getDefaultLocation(userName);
			} catch (server.dataLayer.exceptions.GetDefaultLocationException e) {
				server.utilities.logging.SingletonLogger.getLogger().warning("get default location is failed");
				throw new GetDefaultLocationException(e.getErrorDescription());
			}

	}
	*/

	public Vector<InformationObject> getInformativeData(String userName, WeekDate date, Vector<Area> areas) {
		
		Vector<InformationObject> ans = new Vector<InformationObject>();
		
		try {
			ans.addAll(this.getTemperature(userName, date));
		} catch (GetTemperatureException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning(e.getErrorDescription());
		}
		
		try {
			ans.addAll(this.getMoisture(userName, date));
		} catch (GetMoistureException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning(e.getErrorDescription());
		}

/*		
		try {
			ans.addAll(this.getCrop(userName, date));
		} catch (GetCropException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning(e.getErrorDescription());
		}
*/	
		
		try {
			ans.addAll(this.getPest(userName, date, areas));
		} catch (GetPestException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning(e.getErrorDescription());
		}
			
		try {
			ans.addAll(this._dataManipulator.getSynthetices(userName));
		} catch (GetSyntheticObjectsException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning(e.getErrorDescription());
		}
		
		return ans;
	}

	public Vector<Benchmark> getUserBenchmarks(String userName) throws GetUserBenchmarksException {
		try {
			return this._dataManipulator.getBenchmarks(userName);
		} catch (GetBenchmarksException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning("get benchmarks failed");
			throw new GetUserBenchmarksException(e.getErrorDescription());
		}
	}

	public Set<WeekDate> getValidDates(String userName) {
		return this._dataManipulator.getValidDates(userName);
	}

	
	private Vector<Temperature> getTemperature(String userName, WeekDate date) throws GetTemperatureException {
		Vector<Temperature> ans = new Vector<Temperature>();
		HashMap<String,Vector<Temperature>> temperature = this._dataManipulator.getTemperature(userName, date);
		Set<String> keySet = temperature.keySet();
		Iterator<String> iter = keySet.iterator();
		while (iter.hasNext()) {
			Vector<Temperature> stationTemps = temperature.get(iter.next());
			if ((stationTemps == null) || (stationTemps.size() == 0)) {
				continue;
			}
			Coordinate coordinate = stationTemps.firstElement().getCoordinate();
			ZoomLevel zoomLevel = null;
			try {
				zoomLevel = stationTemps.firstElement().getViewInterval();
			} catch (UnInitializeObjectException e) {
			}
			String meteorologyName = stationTemps.firstElement().getLocationName();
			double sum = 0;
			for (int i = 0; i < stationTemps.size(); i++) {
				sum = sum + stationTemps.get(i).getValue();
			}
			double avg = sum / (double)stationTemps.size();
			try {
				ans.add(new Temperature(coordinate, zoomLevel, avg, date, meteorologyName));
			} catch (UnInitializeObjectException e) {
				scantask.server.utilities.logging.SingletonLogger.getLogger().warning("failed add average temperature");
			} catch (IllegalLocationNameExeption e) {
				scantask.server.utilities.logging.SingletonLogger.getLogger().warning("failed add average temperature");			}
		}
		return ans;
	}
	
	private Vector<Moisture> getMoisture(String userName, WeekDate date) throws GetMoistureException {
		Vector<Moisture> ans = new Vector<Moisture>();
		HashMap<String,Vector<Moisture>> moisture = this._dataManipulator.getMoisture(userName, date);
		Set<String> keySet = moisture.keySet();
		Iterator<String> iter = keySet.iterator();
		while (iter.hasNext()) {
			Vector<Moisture> stationMoistures = moisture.get(iter.next());
			if ((stationMoistures == null) || (stationMoistures.size() == 0)) {
				continue;
			}
			Coordinate coordinate = stationMoistures.firstElement().getCoordinate();
			ZoomLevel zoomLevel = null;
			try {
				zoomLevel = stationMoistures.firstElement().getViewInterval();
			} catch (UnInitializeObjectException e) {
			}
			String meteorologyName = stationMoistures.firstElement().getLocationName();
			double sum = 0;
			for (int i = 0; i < stationMoistures.size(); i++) {
				sum = sum + stationMoistures.get(i).getValue();
			}
			double avg = sum / (double)stationMoistures.size();
			try {
				ans.add(new Moisture(coordinate, zoomLevel, avg, date, meteorologyName));
			} catch (UnInitializeObjectException e) {
				scantask.server.utilities.logging.SingletonLogger.getLogger().warning("failed add average moisture");
			} catch (IllegalLocationNameExeption e) {
				scantask.server.utilities.logging.SingletonLogger.getLogger().warning("failed add average moisture");			}
		}
		return ans;
	}
	
	/*
	private Vector<Crop> getCrop(String userName, WeekDate date) throws GetCropException {
		Measurement interpolate = null;
		Vector<Crop> crop = this._dataManipulator.getCrop(userName, date);
		HashMap<String, Vector<Crop>> cropsMap = new HashMap<String, Vector<Crop>>();
		for (int i = 0; i < crop.size(); i++) {
			if (cropsMap.containsKey(crop.get(i).getType()) == false) {
				cropsMap.put(crop.get(i).getType(), new Vector<Crop>());
			}
			cropsMap.get(crop.get(i).getType()).add(crop.get(i));
		}
		
		for (int i = 0; i < crop.size(); i++) {
			if (crop.get(i).isInterpolated() == true) {
				interpolate = this._interpolator.missingDataInterpolation(cropsMap.get(crop.get(i).getType()), crop.get(i));
				crop.get(i).setValue(interpolate.getValue());
			}
		}
		
		return crop;
	}
	*/

	private Vector<Pest> getPest(String userName, WeekDate date, Vector<Area> areas) throws GetPestException {
		
		Vector<Pest> pest = this._dataManipulator.getPest(userName, date);
		
		// map the area name to the area object
		HashMap<String, Area> areaMap = new HashMap<String, Area>();
		for (int i = 0; i < areas.size(); i++) {
			areaMap.put(areas.get(i).getName(), areas.get(i));
		}
		
		// map of area name to map of pest type to vector of pests of this type (NOT Interpolated)
		HashMap<String, HashMap<String, Vector<Pest>>> areaToPestNameToNotInterPests = this.getMapOfAreaToPestNameToNotInterpolatedPests(pest, areaMap);
		
		// map of area name to map of pest type to vector of pest of this type (INTERPOLATED)
		HashMap<String, HashMap<String, Vector<Pest>>> areaToPestNameToInterPests = this.getMapOfAreaToPestNameToInterpolatedPests(pest, areaMap, areaToPestNameToNotInterPests);

		// set temporary location of the measurements as the center of the area
		Set<String> keySet = areaToPestNameToNotInterPests.keySet();
		Iterator<String> iter = keySet.iterator();
		while (iter.hasNext()) {
			String currAreaName = iter.next();
			this.setTemporaryPestCoordinateToAreaCenter(currAreaName, areaMap, areaToPestNameToNotInterPests.get(currAreaName));
		}
		// set temporary location of the measurements as the center of the area		
		keySet = areaToPestNameToInterPests.keySet();
		iter = keySet.iterator();
		while (iter.hasNext()) {
			String currAreaName = iter.next();
			this.setTemporaryPestCoordinateToAreaCenter(currAreaName, areaMap, areaToPestNameToInterPests.get(currAreaName));
		}
		
		// performing the interpolation and update the appropriate map
		this.performInterploation(areaToPestNameToInterPests, this.getMapOfPestTypeToNotInterpolatedPest(areaToPestNameToNotInterPests));
		
		// calculate average of the measurements per type in area
		HashMap<String, Vector<Pest>> areaToAvgPests = new HashMap<String, Vector<Pest>>();
		// do it first for the interpolation measurements
		this.calcAverageAndMapToArea(areaToPestNameToInterPests, areaToAvgPests);
		// do it for the real measurements
		this.calcAverageAndMapToArea(areaToPestNameToNotInterPests, areaToAvgPests);
		
		// calculate the final location in the area and set the radius of each pest measurement
		this.setFinalLocationAndRadiusOfPest(areaMap, areaToAvgPests);
		
		Vector<Pest> ans = new Vector<Pest>();
		keySet = areaToAvgPests.keySet();
		iter = keySet.iterator();
		while (iter.hasNext()) {
			ans.addAll(areaToAvgPests.get(iter.next()));
		}
		
		return ans;
	}
	
	private HashMap<String, HashMap<String, Vector<Pest>>> getMapOfAreaToPestNameToNotInterpolatedPests(Vector<Pest> pest, HashMap<String, Area> areaMap) {
		// map of area name to map of pest type to vector of pests of this type (NOT Interpolated)
		HashMap<String, HashMap<String, Vector<Pest>>> ans = new HashMap<String, HashMap<String,Vector<Pest>>>();
		for (int i = 0; i < pest.size(); i++) {
			// in this part do not handle interpolate measurements 
			if (pest.get(i).isInterpolated() == true) {
				continue;
			}
			String currAreaName = pest.get(i).getLocationName();
			// if the pest area exists in this user areas
			if (areaMap.containsKey(currAreaName)) {
				String currPestType = pest.get(i).getCleanType();
				// if the area does not exist yet in the map
				if (ans.containsKey(currAreaName) == false) {
					ans.put(currAreaName, new HashMap<String, Vector<Pest>>());
				}
				// if the pest type does not exist yet in the map
				if (ans.get(currAreaName).containsKey(currPestType) == false) {
					ans.get(currAreaName).put(currPestType, new Vector<Pest>());
				}
				ans.get(currAreaName).get(currPestType).add(pest.get(i));
			}
		}
		return ans;
	}
	
	private HashMap<String, HashMap<String, Vector<Pest>>> getMapOfAreaToPestNameToInterpolatedPests(Vector<Pest> pest, 
			HashMap<String, Area> areaMap, HashMap<String, HashMap<String, Vector<Pest>>> areaToPestNameToNotInterPests) {
		
		// map of area name to map of pest type to vector of pests of this type (INTERPOLATED)
		HashMap<String, HashMap<String, Vector<Pest>>> ans = new HashMap<String, HashMap<String,Vector<Pest>>>();
		for (int i = 0; i < pest.size(); i++) {
			// in this part handle interpolate measurements ONLY 
			if (pest.get(i).isInterpolated() == false) {
				continue;
			}
			String currAreaName = pest.get(i).getLocationName();
			// if the pest area exists in this user areas
			if (areaMap.containsKey(currAreaName)) {
				String currPestType = pest.get(i).getCleanType();

				// If exist NOT interpolated measurement of the same type and area, shall not perform interpolation for this measurement
				if (areaToPestNameToNotInterPests.containsKey(currAreaName) == true) {
					if (areaToPestNameToNotInterPests.get(currAreaName).containsKey(currPestType) == true) {
						if (areaToPestNameToNotInterPests.get(currAreaName).get(currPestType).size() > 0) {
							continue;
						}
					}
				}
				
				// if the area does not exist yet in the map
				if (ans.containsKey(currAreaName) == false) {
					ans.put(currAreaName, new HashMap<String, Vector<Pest>>());
				}
				// if the pest type does not exist yet in the map
				if (ans.get(currAreaName).containsKey(currPestType) == false) {
					ans.get(currAreaName).put(currPestType, new Vector<Pest>());
				}
				ans.get(currAreaName).get(currPestType).add(pest.get(i));
			}
		}
		return ans;
	}
	
	private void setTemporaryPestCoordinateToAreaCenter(String areaName, HashMap<String, Area> areas, HashMap<String, Vector<Pest>> areaToPest) {
		Set<String> keys = areaToPest.keySet();
		Iterator<String> iter = keys.iterator();
		while (iter.hasNext()) {
			String currKey = iter.next();
			Area currArea = areas.get(areaName);
			if (currArea == null) {
				// AdirD - Cannot reach to this point
				scantask.server.utilities.logging.SingletonLogger.getLogger().warning("get area from map falied, skip current pest mesurement");
				continue;
			}
			Vector<Pest> currAreaPests = areaToPest.get(currKey);
			for (int i = 0; i < currAreaPests.size(); i++) {
				try {
					currAreaPests.get(i).setCoordinate(currArea.getCenterCoordinate());
				} catch (UnInitializeObjectException e) {
					scantask.server.utilities.logging.SingletonLogger.getLogger().warning("get center coordinate of area " + currArea.getName() + " falied, skip current pest mesurement");
					continue;
				}
			}
		}
		
	}
	
	private HashMap<String, Vector<Pest>> getMapOfPestTypeToNotInterpolatedPest(HashMap<String, HashMap<String, Vector<Pest>>> areaToPestNameToNotInterPests) {
		HashMap<String, Vector<Pest>> ans = new HashMap<String, Vector<Pest>>();
		Set<String> areaKeySet = areaToPestNameToNotInterPests.keySet();
		Iterator<String> areaIter = areaKeySet.iterator();
		while (areaIter.hasNext()) {
			HashMap<String, Vector<Pest>> currAreaPests = areaToPestNameToNotInterPests.get(areaIter.next());
			Set<String> typeKeySet = currAreaPests.keySet();
			Iterator<String> typeIter = typeKeySet.iterator();
			while (typeIter.hasNext()) {
				String currType = typeIter.next();
				if (ans.containsKey(currType) == false) {
					ans.put(currType, new Vector<Pest>());
				}
				ans.get(currType).addAll(currAreaPests.get(currType));
			}
		}
		return ans;
	}
	
	private void performInterploation(HashMap<String, HashMap<String, Vector<Pest>>> areaToPestNameToInterPests, 
			HashMap<String, Vector<Pest>> typeToPestNotInter) {
		
		Set<String> areaKeySet = areaToPestNameToInterPests.keySet();
		Iterator<String> areaIterator = areaKeySet.iterator();
		while (areaIterator.hasNext()) {
			String currArea = areaIterator.next();
			Set<String> typeKeySet = areaToPestNameToInterPests.get(currArea).keySet();
			Iterator<String> typeIter = typeKeySet.iterator();
			while (typeIter.hasNext()) {
				String currType = typeIter.next();
				// if this type does not have real measurements
				if (typeToPestNotInter.containsKey(currType) == false) {
					scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed interpolate pest of type " + currType + " this type does not exist in non interpolated measurements");
					areaToPestNameToInterPests.get(currArea).get(currType).removeAllElements();
					continue;
				}
				Vector<Pest> failedInterpolatedPests = new Vector<Pest>();
				Vector<Pest> currPestsToInterpolate = areaToPestNameToInterPests.get(currArea).get(currType);
				Vector<Pest> realMeasurement = typeToPestNotInter.get(currType);
				for (int i = 0; i < currPestsToInterpolate.size() ; i++) {
					try {
						this._interpolator.missingDataInterpolation(realMeasurement, currPestsToInterpolate.get(i));
					} catch (InterpolationException e) {
						scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed interpolate pest of type " + currType);
						failedInterpolatedPests.add(currPestsToInterpolate.get(i));
					}
				}
				// remove the pests interpolated measurements that failed interpolate
				for (int i = 0; i < failedInterpolatedPests.size(); i++) {
					areaToPestNameToInterPests.get(currArea).get(currType).remove(failedInterpolatedPests.get(i));
				}
			}
			
		}
	}
	
	private void calcAverageAndMapToArea(HashMap<String, HashMap<String, Vector<Pest>>> areaToPestTypeToPests, HashMap<String, Vector<Pest>> result) {
		Set<String> areaKeySet = areaToPestTypeToPests.keySet();
		Iterator<String> areaIter = areaKeySet.iterator();
		while (areaIter.hasNext()) {
			String currArea = areaIter.next();
			if (result.containsKey(currArea) == false) {
				result.put(currArea, new Vector<Pest>());
			}
			Set<String> typeKeySet = areaToPestTypeToPests.get(currArea).keySet();
			Iterator<String> typeIter = typeKeySet.iterator();
			while (typeIter.hasNext()) {
				String currType = typeIter.next(); 
				Pest avgPest = this.getAveragePest(areaToPestTypeToPests.get(currArea).get(currType));
				if (avgPest == null) {
					scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed perform average to pest type " + currType + " in area " + currArea);
					continue;
				}
				// if the average of this type in the area is 0 then drop it
				if (avgPest.getValue() <= 0) {
					scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Drop average pest type " + currType + " in area " + currArea + " because the average is equal or less than zero");
					continue;
				}
				result.get(currArea).add(avgPest);
			}
		}
	}
	
	private Pest getAveragePest(Vector<Pest> pests) {
		if ((pests == null) || (pests.size() == 0)) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed perform average, the pests vector is null or size 0");
			return null;
		}
		Coordinate coordinate = pests.firstElement().getCoordinate();
		ZoomLevel zoomLevel = null;
		try {
			zoomLevel = pests.firstElement().getViewInterval();
		} catch (UnInitializeObjectException e) {
		}
		WeekDate date = null;
		try {
			date = pests.firstElement().getDate();
		} catch (UnInitializeObjectException e) {
		}
		boolean interpolated = pests.firstElement().isInterpolated();
		String type = pests.firstElement().getCleanType();
		String areaName = pests.firstElement().getLocationName();
		
		Pest ans = null;
		double sum = 0;
		for (int i = 0; i < pests.size(); i++) {
			sum = sum + pests.get(i).getValue();
		}
		double avg = sum / (double)pests.size();
		try {
			ans = new Pest(coordinate, zoomLevel, avg, date, interpolated, type, areaName);
		} catch (UnInitializeObjectException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed create average pest");
			return null;
		} catch (InvalidMeasurementTypeException e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed create average pest");
			return null;
		} catch (IllegalLocationNameExeption e) {
			scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed create average pest");
			return null;
		}
		return ans;
	}
	
	private void setFinalLocationAndRadiusOfPest(HashMap<String, Area> areaMap, HashMap<String, Vector<Pest>> result) {
		Set<String> keySet = result.keySet();
		Iterator<String> iter = keySet.iterator();
		while (iter.hasNext()) {
			String currAreaName = iter.next();
			Area area = areaMap.get(currAreaName);
			if (area != null) {
				try {
					this.setFinalPestsLocationAndRadiusAccordingToClockOrder(area.getVertixes(), area.getCenterCoordinate(), result.get(currAreaName));
				} catch (UnInitializeObjectException e) {
					scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed set final location and radius of pests because the area " + area.getName() + " vertexes vector is null");
				}
			}
		}
	}
	
	private boolean isInside(Vector<Coordinate> pointsOfPol, Coordinate coordinate) {
		final int digitsAfterPointConv = 1000000;
		Polygon poly = new Polygon();
		for (int i = 0; i < pointsOfPol.size(); i++) {
			poly.addPoint((int)(pointsOfPol.get(i).getX() * digitsAfterPointConv), (int)(pointsOfPol.get(i).getY() * digitsAfterPointConv));
		}
		return poly.contains((int)(coordinate.getX() * digitsAfterPointConv),(int)(coordinate.getY() * digitsAfterPointConv));
	}
	
	public void setFinalPestsLocationAndRadiusAccordingToClockOrder(Vector<Coordinate> areaVertexes, Coordinate areaCenter, Vector<Pest> resultPests) {
		int amount = resultPests.size();
		int radius = MAX_RADIUS;
		double d;
		double theta;
		int alreadyPlaced;
		int cycle;
		double space = 0;
		boolean success = false;
		while (success == false) {
			success = true;
			d = 3 * radius;
			theta = 1;
			alreadyPlaced = 1;
			cycle = 1;
			while ((alreadyPlaced < amount) && (success == true)) {
				if (radius > MIN_RADIUS) {
					success = false;
				}
				theta = 0;
				space = 360.0 / (cycle * 4.0);
				while ((alreadyPlaced < amount) && (theta + space <= 360)) {
					Coordinate coordinate;
					try {
						coordinate = this.calcAppropriateLatAndLon(areaCenter, d, theta);
					} catch (InvalidCoordinateValueException e) {
						scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed create new coordinate for the final location of the pest measurement");
						continue;
					}
					// if the center of the cylinder is inside the area
					if (this.isInside(areaVertexes, coordinate)) {
						boolean insideFlag = true;
						Coordinate limitCoordinate;
						int alpha = 0;
						int step = 10;
						while ((insideFlag) && (alpha < 360)) {
							try {
								limitCoordinate = this.calcAppropriateLatAndLon(coordinate, radius, alpha);
								insideFlag = insideFlag & this.isInside(areaVertexes, limitCoordinate);
							} catch (InvalidCoordinateValueException e1) {
								scantask.server.utilities.logging.SingletonLogger.getLogger().warning("Failed create new coordinate for the final location of the pest measurement, when trying to check if the limit inside");
								insideFlag = false;
							}
							alpha = alpha + step; 
						}

						if (insideFlag) {
							try {
								resultPests.get(alreadyPlaced).setCoordinate(coordinate);
							} catch (UnInitializeObjectException e) {
								//AdirD - impossible to reach here
							}
							alreadyPlaced ++;
							theta = theta + space;
							success = true;
							continue;
						}
					}
					theta ++;
				}
				cycle ++;
				d = d + 3 * radius;
			}
			radius --;
		}
		for (int i = 0; i < resultPests.size(); i++) {
			resultPests.get(i).setRadius(radius);
		}
	}
	
	
	private Coordinate calcAppropriateLatAndLon(Coordinate center, double radius, double theta) throws InvalidCoordinateValueException {
		double xRad = Math.toRadians(center.getX());
		double yRad = Math.toRadians(center.getY());
		double r = 6371000.0;
		double lat = 0;
		double lon = 0;
		double thetaRad = 0;
		thetaRad = Math.toRadians(theta);
		lat = Math.asin(Math.sin(yRad)*Math.cos(radius / r)+Math.cos(yRad)*Math.sin(radius / r) * Math.cos(thetaRad));
		lon = xRad + Math.atan2(Math.sin(thetaRad)*Math.sin(radius / r)*Math.cos(yRad), Math.cos(radius / r) - Math.sin(yRad)*Math.sin(lat));
		lon = (lon + 3 * Math.PI) % (2 * Math.PI) - Math.PI; //NORMILZE
		Coordinate coordinate;
		coordinate = new Coordinate(Math.toDegrees(lon), Math.toDegrees(lat));
		return coordinate;
	}
	
	
}
